﻿// ComponentPackage.cs
//
// Copyright 2008 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.OLE.Interop;
using IServiceProvider = System.IServiceProvider;

namespace YaTools.Yaml.LanguagePackage
{
    /// <summary>
    /// Adapter to inteface between COM IOleComponent model and .NET for OnIdle()
    /// </summary>
    internal sealed class IdleHandler : IDisposable, IOleComponent
    {
        private readonly Action<bool> _callback;
        private readonly IOleComponentManager _componentManager;
        private uint _componentID;

        public IdleHandler(IServiceProvider serviceProvider, Action<bool> idleCallback)
        {
            Utility.ValidateArgumentNotNull(serviceProvider, "serviceProvider");
            Utility.ValidateArgumentNotNull(idleCallback, "idleCallback");
            _callback = idleCallback;

            _componentManager = serviceProvider.GetService(typeof (SOleComponentManager))
                                as IOleComponentManager;
            if (_componentManager == null)
                Trace.WriteLine("Unable to retrieve OleComponentManager service, no OnIdle processing");
            else
                RegisterForIdleTime();
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
        }

        #endregion

        #region IOleComponent Members

        int IOleComponent.FContinueMessageLoop(uint uReason, IntPtr pvLoopData, MSG[] pMsgPeeked)
        {
            return 1;
        }

        int IOleComponent.FDoIdle(uint grfidlef)
        {
            bool periodic = (grfidlef & (uint) _OLEIDLEF.oleidlefPeriodic) != 0;
            _callback(periodic);
            return 0;
        }

        int IOleComponent.FPreTranslateMessage(MSG[] pMsg)
        {
            return 0;
        }

        int IOleComponent.FQueryTerminate(int fPromptUser)
        {
            return 1;
        }

        int IOleComponent.FReserved1(uint dwReserved, uint message, IntPtr wParam, IntPtr lParam)
        {
            return 1;
        }

        IntPtr IOleComponent.HwndGetWindow(uint dwWhich, uint dwReserved)
        {
            return IntPtr.Zero;
        }

        void IOleComponent.OnActivationChange(IOleComponent pic, int fSameComponent,
                                              OLECRINFO[] pcrinfo, int fHostIsActivating, OLECHOSTINFO[] pchostinfo,
                                              uint dwReserved)
        {
        }

        void IOleComponent.OnAppActivate(int fActive, uint dwOtherThreadID)
        {
        }

        void IOleComponent.OnEnterState(uint uStateID, int fEnter)
        {
        }

        void IOleComponent.OnLoseActivation()
        {
        }

        void IOleComponent.Terminate()
        {
        }

        #endregion

        private void RegisterForIdleTime()
        {
            if (_componentID == 0 && _componentManager != null)
            {
                var crinfo = new OLECRINFO[1];
                crinfo[0].cbSize = (uint) Marshal.SizeOf(typeof (OLECRINFO));
                crinfo[0].grfcrf = (uint) _OLECRF.olecrfNeedIdleTime |
                                   (uint) _OLECRF.olecrfNeedPeriodicIdleTime;
                crinfo[0].grfcadvf = (uint) _OLECADVF.olecadvfModal |
                                     (uint) _OLECADVF.olecadvfRedrawOff |
                                     (uint) _OLECADVF.olecadvfWarningsOff;
                crinfo[0].uIdleTimeInterval = 1000;
                int hr = _componentManager.FRegisterComponent(this, crinfo, out _componentID);
                CheckHResult(hr);
            }
        }

        private static void CheckHResult(int hr)
        {
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);
        }

        // ReSharper disable UnusedParameter.Local
        private void Dispose(bool disposing)
        {
            if (_componentID != 0)
            {
                _componentManager.FRevokeComponent(_componentID);
            }
        }
        // ReSharper restore UnusedParameter.Local
    }
}